7.7.1 Constructing application contexts

应用程序上下文构造器(对于特定的应用程序上下文类型)通常将字符串或字符串数组作为资源的位置路径,例如像上下文中定义的XML文件。
当路径没有特定的前缀时,从路径中构建的Resource的类型和加载bean定义的方式取决于特定的应用程序上下文。比如,如果你像下列一样创建了一个ClassPathXmlApplicationContext

ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");

ClassPathResource被使用时,bean的定义也会从类路径中被加载。但是如如果你像下列这样创建了一个FileSystemXmlApplication

ApplicationContext ctx =
    new FileSystemXmlApplicationContext("conf/appContext.xml");

bean的定义将会从文件系统中加载,这种情况下位置是相对于当前的工作目录。
需要注意,当路径使用了特殊的类路径前缀或是标准的URL前缀时,会覆盖被创建的Resource的默认类型。所以,下面的FileSystemXmlApplicationContext

ApplicationContext ctx =
    new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");

实际上会从类路径下加载bean定义。然而,它仍然是FileSystemXmlApplicationContext。如果它之后还被用作为ResourceLoader,任何没有前缀的路径依旧会被当做文件系统路径。

Constructing ClassPathXmlApplicationContext instances - shortcuts

ClassPathXmlApplicationContext暴露出了一些构造函数可以很方便的初始化。其基本思想是只提供一个字符串数组,它只包含XML文件本身的文件名(没有前导路径信息),还提供一个Class; ClassPathXmlApplicationContext将从所提供的类中派生路径信息。
这个例子希望能够解释的清楚。如果有一个目录结构如下:
com
└ foo
├ services.xml
├ daos.xml
└ MessengerService.class
'services.xml''daos.xml'中定义的bean组成的ClassPathXmlApplicationContext实例可以像这样实例化...

ApplicationContext ctx = new ClassPathXmlApplicationContext(
    new String[] {"services.xml", "daos.xml"}, MessengerService.class);

请查看ClassPathXmlApplicationContext的Java文档查询更多有关构造函数的信息。

7.7.2 Wildcards in application context constructor resource paths

传入到应用程序上下文构造器中的资源路径值可以是只映射一个目标资源的简单路径(如上文所示),或者可以包含特殊的“classpath *:”前缀和/或内部的Ant- style的正则表达式(使用Spring的PathMatcher工具类进行匹配)。后者的两种情况都是有效的通配符。
此机制的一个用途是在执行组件式应用程序组装时。 所有组件都可以将上下文定义片段“发布”到众所周知的位置路径,并且当最终应用程序上下文使用通过classpath *:前缀的相同路径创建时,所有组件片段将被自动拾取。
值得注意的是,这个通配符特别用来在应用程序上下文的构造函数中指定资源路径(或是直接使用PathMatcher工具类),并且在应用程序构造的时候解析路径。他与Resource类型本身无关。不能用classpath*:前缀的路径去创建一个实际的Resource对象,因为一个Resource对象只能表示一个资源。

Ant-style Patterns

当一个路径含有Ant风格的格式,比如:
/WEB-INF/*-context.xml
com/mycompany//applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/
/applicationContext.xml
解析器会遵循一个更复杂的但是既定的程序来尝试解析通配符。 它为最后一个非通配符段的路径片段生成一个资源,并从中获得一个URL。如果此URL不是jar:URL或其他容器特定的变体(例如,WebLogic中的zip:,WebSphere中的wsjar:等),则从中获取java.io.File,并通过遍历文件系统来解析通配符。如果是jar URL,那么解析器要从中获取一个java.net.JarURLConnection,或者手动解析jar URL,然后遍历jar文件中的内容来解析通配符。

Implications on portability

如果一个指定的路径是一个file URL(无论是显示的,还是由于ResouceLoader隐式的加载为文件系统的),那么通配符都可以保证苛移植的。 如果一个指定的路径是一个类路径,那么解析器会通过Classloader.getResource()来回去最后一个非通配符的URL片段。由于这只有路径上的一个节点(并非是最后的文件),实际并未确定最后返回URL的类型。在实践中,当类路径被解析成文件系统时,类型会是表示路径的java.io.File。如果类路径被解析成为jar包,那么类型将是jar URL。这仍然是咳移植的。
如果一个jar URL是从最后一个不含通配符的片段中获得的,那么解析器必须能从它上获取java.net.JarURLConnection,或是手动的解析成jar URL.这样才能访问jar包的内容并解析通配符。这在大多数环境中都是可以的,但是在少数中会失败。强烈建议来自jar包的通配符解析需要在你使用它之前经过充分的测试。

The classpath*:prefix

当创建一个基于XML的应用程序上下文时,表示路径的字符串可以使用claspath:前缀:

ApplicationContext ctx =
    new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");

这个特定的前缀特指了类路径下所有符合制定名字的资源(在内部,这本是还是通过调用ClassLoader.getResources()实现的),然后,在最终的应用程序上下文中合并。

通配符类路径依赖于底层类加载器的getResources()方法。目前大多数的应用程序服务器提供了他们自己的类加载器的实现,他们在处理jar包的时候可能会存在差异。测试classpath*是否工作载可以通过类加载器从classpath中的Jar中加载文件:getClass().getClassLoader().getResources("<someFileInsideTheJar>")。用有相同名字但是在不同路径下的两个文件进行此测试。如果返回的结果不正确,请检查应用程序服务器的文档来了解可能影响类加载行为的设置。

classpath*:前缀也可以和其他路径下的PathMather结合使用。例如:classpath*:META-INF/*-beans.xml
在这种情况下,解析的策略相当的简单:在最后一个非通配符的路径片段上调用ClassLoader.getResources()在类加载器的层次结构中获取所有符合的资源,然后在每个通配符的子路径上,用上述PathMatcher描述的策略解析。

Other notes relating to wildcards

请注意,如果目标文件不在文件系统中,那么classpath*:和Ant风格的Pattern结合使用之前,必须要至少有一个根目录才能确保其稳定的工作。这意味着像classpath*:*.xml不会从jar包的根路径检索,而只会从解压包里的根路径开始检索。这是源自ClassLoader.getResources()方法的限制,当传入空的路径字符串时,它只会返回文件系统的位置(指明了搜索的根路径)。
如果搜索的根路径在多个类路径下,那么Ant风格的样式和classpath:的资源将可能找不到匹配的组员。这是因为像
com/mycompany/package1/service-context.xml
的资源只可能存在一个路径下,而如果要接下像
classpath:com/mycompany/**/service-context.xml的路径,解析器将处理由getResource("com / mycompany");返回的(第一个)URL。如果此基础包节点存在于多个类加载器位置中,则实际最终要想的资源可能不在这下面。因此,最好在这种情况下,使用具有相同Ant风格样式的"classpath *:",这将搜索包含根包的所有类路径位置。

7.7.3 FileSystemResource caveats

一个未绑定到FileSystemApplicationCOntextFileSystemResource(也就是说,FileSystemApplicationContext不是实际上的ResourceLoader)将会按照你的预期去处理绝对路径和虚拟路径。
相对路径是相对于当前的工作路径,而绝对路径是相对于文件系统的根路径。
然而,为了向前兼容的原因。当FileSystemApplicationContextResourceLoader时,就不同了。
FileSystemApplicationContext会强制所有绑定的FileSystemResource实例将所有位置路径视为相对路径,无论它们是否以一个前导斜杠开始。实践中,这意味着以下是等价的:

ApplicationContext ctx =
    new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("/conf/context.xml");

下面的例子:(有所不同,一个是相对路径,另一个是绝对路径):

FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");

在实践中,如果需要真正的绝对文件系统路径,最好放弃使用FileSystemResource/ FileSystemXmlApplicationContext的绝对路径,并使用file:前缀强制使用UrlResource

// 实际上,上下文类型无关紧要,资源总会被解析成URLResource
ctx.getResource("file:///some/resource/path/myTemplate.txt");
// 强制让FileSystemXmlApplicationContext加载UrlResource
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("file:///conf/context.xml");